<?php

namespace app\controllers;

use app\service\tool\ConvertService;
use app\service\tool\UtilsService;
use Yii;
use yii\helpers\ArrayHelper;
use yii\web\Controller;
use yii\web\Response;

/**
 * Class BaseController
 * @package app\controllers
 */
class BaseController extends Controller
{

    /**
     * 错误展示默认错误码 && 原因
     * @var array
     */
    private $errorReasons = [
        'default' => [
            '手抖打错了。',
            '链接过了保质期。',
        ],
        403 => [
            '登录超时。',
            '此页面您没有权限查看。',
            '确实需要此页面，请联系站点管理员。',
        ],
        401 => [
            '登录超时。',
            '此页面您没有权限查看。',
            '确实需要此页面，请联系站点管理员。'
        ],
        404 => [
            '手抖打错了。',
            '链接过了保质期。',
            '尝试多次无果，可直接联系站点管理员。'
        ],
    ];

    /**
     * 请求参数格式化
     * @param $param
     * @param bool $format
     * @return float|int|string|array
     */
    protected function formatParams($param, $format = true)
    {

        // 数据是默认并且是 数组
        if (is_array($param) && ($format === true || $format === 'array' || $format === 'arr'))
            return $param;

        $list = [
            'string' => ['str', 'string', 'strval'], // 字符
            'integer' => ['int', 'intval', 'integer', 'numeric'], // 整数
            'float' => ['float', 'floatval', 'double', 'doubleval'], // 小数
            'bool' => ['bool', 'boolean'] // 布尔类型
        ];
        // 拿一个集合列表
        $listAll = [];
        foreach ($list as $k => $v) {
            $listAll = array_merge($listAll, $v);
        }

        ### 自定义转化 - 数字
        // 数据不是合法格式化，是小数
        if (!in_array($format, $listAll) && ConvertService::isDecimal($param)) {
            return floatval($param);
        }
        // 数据不是合法格式化，是整数
        if (!in_array($format, $listAll) && is_numeric($param)) {
            return intval($param);
        }
        // 数据不是合法格式化，是字符
        if (!in_array($format, $listAll) && is_string($param)) {
            return strval($param);
        }

        ### 自定义转化 - 字符
        // 参数非数组，要求转为字符
        if (!is_array($param) && in_array($format, $list['string'])) {
            return trim(strval($param));
        }
        // 参数数组，要求转为字符 - 化为[JSON]
        if (is_array($param) && in_array($format, $list['string'])) {
            return json_encode($param, JSON_UNESCAPED_UNICODE);
        }

        ### 自定义转化 - 小数
        // 参数非数组，要求转为小数
        if (!is_array($param) && in_array($format, $list['float'])) {
            return floatval($param);
        }
        // 参数数组，要求转为小数
        if (is_array($param) && in_array($format, $list['float'])) {
            return floatval($param);
        }

        ### 自定义转化 - 整数
        // 参数非数组，要求转为整数
        if (!is_array($param) && in_array($format, $list['integer'])) {
            return intval($param);
        }
        // 参数数组，要求转为整数
        if (is_array($param) && in_array($format, $list['integer'])) {
            return intval($param);
        }

        ### 自定义转化 - 布尔类型
        if (in_array($format, $list['bool'])) {
            return ConvertService::toBoolVal($param);
        }

        // 其余不做转换
        return $param;
    }

    /**
     * 获取[post]参数
     * @param array|string|integer $param 要获取的参数[key]
     * @param mixed $default 初始值
     * @param bool $format 是否格式化
     * @return array|float|int|mixed|string|null
     */
    protected function post($param = [], $default = null, $format = true)
    {

        ### 格式化[Request]参数和方法参数
        $requestData = Yii::$app->request->post();
        // 备份最初始基础[要获取的参数]
        $basParam = $param;
        // 组成数组去空
        if (!is_array($param)) $param = array_filter(explode(',', strval($param)));
        // [要获取的参数]为空，则需要返回全部请求参数
        if (empty($param)) $param = array_keys($requestData);
        // [要获取的参数]为空返回空
        if (empty($param)) return [];

        ### 执行获取格式化
        $end = [];
        foreach ($param as $k => $v) {

            ### 查询到具体键
            try {
                $end[$v] = ArrayHelper::getValue($requestData, $v);
            } catch (\Exception $error) {
                $end[$v] = null;
            }
            // 有值格式化
            if (isset($end[$v])) $end[$v] = $this->formatParams($end[$v], $format);
            // 没有默认
            else if (empty($end[$v])) $end[$v] = $default;
        }

        return is_array($basParam) ? $end : current($end);
    }

    /**
     * 获取[get]参数
     * @param array|string|integer $param 要获取的参数[key]
     * @param mixed $default 初始值
     * @param bool $format 是否格式化
     * @return array|float|int|mixed|string|null
     */
    protected function get($param = [], $default = null, $format = true)
    {

        ### 格式化[Request]参数和方法参数
        $requestData = Yii::$app->request->get();
        // 备份最初始基础[要获取的参数]
        $basParam = $param;
        // 组成数组去空
        if (!is_array($param)) $param = array_filter(explode(',', strval($param)));
        // [要获取的参数]为空，则需要返回全部请求参数
        if (empty($param)) $param = array_keys($requestData);
        // [要获取的参数]为空返回空
        if (empty($param)) return [];

        ### 执行获取格式化
        $end = [];
        foreach ($param as $k => $v) {

            ### 查询到具体键
            try {
                $end[$v] = ArrayHelper::getValue($requestData, $v);
            } catch (\Exception $error) {
                $end[$v] = null;
            }
            // 有值格式化
            if (isset($end[$v])) $end[$v] = $this->formatParams($end[$v], $format);
            // 没有默认
            else if (empty($end[$v])) $end[$v] = $default;
        }

        return is_array($basParam) ? $end : current($end);
    }

    /**
     * 获取[request]参数|从[get]、[post]获取
     * @param array $param 要获取的参数[key]
     * @param mixed $default 初始值
     * @param bool $format 是否格式化
     * @return array|string|null
     */
    protected function request($param = [], $default = null, $format = true)
    {

        ### 格式化[Request]参数和方法参数
        // 获取请求参数
        $requestData = [];
        if (\Yii::$app->request->getIsGet()) {
            $requestData = $this->get($param, $default, $format);
        } else if (\Yii::$app->request->getIsPost()) {
            $requestData = $this->post($param, $default, $format);
        }

        return $requestData;
    }

    /**
     * api返回的json
     * @param $message
     * @param $code
     * @param $data
     * @param $jsonStr
     * @return \yii\console\Response|Response
     */
    protected function jsonEncode($message = '', $code = 0, $data = [], $jsonStr = null)
    {

        // 最终返回
        $result = [
            'msg' => $message, // 错误信息
            'no' => $code,  // 错误 code
            'data' => $data
        ];

        // jsonp 函数式返回
        if (!$jsonStr) {

            $result = json_encode($result);
        } else {

            $result = $jsonStr . '(' . json_encode($result) . ');';
        }

        //设置响应对象
        Yii::$app->response->clear();
        $response = Yii::$app->response;
        $response->format = Response::FORMAT_JSON;
        $response->content = $result;
        return $response;
    }

    /**
     * 操作成功 json 返回
     * @param $message
     * @param $data
     * @param $jsonStr
     * @return \yii\console\Response|Response
     */
    protected function jsonSuccess($message = '', $data = [], $jsonStr = null)
    {

        // 成功信息空的就是 成功了
        $message = $message ? $message : '成功';
        // 默认code就200
        return $this->jsonEncode($message, 200, $data, $jsonStr);
    }

    /**
     * 操作失败 json 返回
     * @param $message
     * @param $code
     *  ` 200: OK。一切正常。
     *  ` 201: 响应 POST 请求时成功创建一个资源。Location header 包含的URL指向新创建的资源。
     *  ` 204: 该请求被成功处理，响应不包含正文内容 (类似 DELETE 请求)。
     *  ` 304: 资源没有被修改。可以使用缓存的版本。
     *  ` 400: 错误的请求。可能通过用户方面的多种原因引起的，例如在请求体内有无效的JSON 数据，无效的操作参数，等等。
     *  ` 401: 验证失败。需要重新登录
     *  ` 403: 已经经过身份验证的用户不允许访问指定的 API 末端。
     *  ` 404: 所请求的资源不存在。
     *  ` 405: 不被允许的方法。 请检查 Allow header 允许的HTTP方法。
     *  ` 415: 不支持的媒体类型。 所请求的内容类型或版本号是无效的。
     *  ` 422: 数据验证失败 (例如，响应一个 POST 请求)。 请检查响应体内详细的错误消息。
     *  ` 429: 请求过多。 由于限速请求被拒绝。
     *  ` 500: 内部服务器错误。 这可能是由于内部程序错误引起的。
     * @param $data
     * @param null $jsonStr
     * @return \yii\console\Response|Response
     */
    protected function jsonFail($message = '', $code = 400, $data = [], $jsonStr = null)
    {

        // 错误code默认赋值
        $code = empty($code) ? 400 : $code;

        // 错误信息默认赋值
        if (empty($message) || $code == 500 || !is_numeric($code)) {
            $data['error_hint'] = empty($message) ? '未知错误' : $message;
            $message = '请求频繁，稍后尝试';
        }

        return $this->jsonEncode($message, $code, $data, $jsonStr);
    }

    /**
     * json自定义返回
     * @param $data
     */
    protected function jsonCustom($data)
    {

        //设置响应对象
        $response = Yii::$app->response;
        $response->format = Response::FORMAT_JSON;
        $response->content = is_array($data) ? json_encode($data, JSON_UNESCAPED_UNICODE) : $data;
    }

    /**
     * 返回XML
     * @param array $data
     * @return string
     */
    protected function xmlReturn($data = [])
    {

        if (empty($data)) return '';

        $xml = '<xml>';
        foreach ($data as $key => $val) {

            if (is_numeric($val)) {
                $xml .= "<" . $key . ">" . $val . "</" . $key . ">";
            } else {
                $xml .= "<" . $key . "><![CDATA[" . $val . "]]></" . $key . ">";
            }
        }

        $xml .= '</xml>';

        // 设置响应对象
        $response = Yii::$app->response;
        $response->format = Response::FORMAT_XML;
        return $response->content = $xml;
    }

    /**
     * 获取分页[HTML]
     * @param int $pageSize
     * @param int $count
     * @param boolean $small
     * @return string|null
     */
    protected function getPageHtml($pageSize = 0, $count = 0, $small = true)
    {

        $opt = [
            'maxButtonCount' => 5
        ];
        // 不是迷你版 - 加载配置
        if (!$small) {
            $opt = array_merge($opt, [
                'loadGoPage' => true
            ]);
        }
        return UtilsService::getPageHtml($pageSize, $count, $opt);
    }

    /**
     * 页面展示错误
     * @param int $code 错误码
     * @param string $message 错误信息, 请参考方法[$this->jsonFail]中的[code]
     * @param array $option 其他配置
     *  ` redirectList - 可跳转的连接列表
     *  ` template - 错误模板
     *  ` reasons - 可能的原因
     * @return string
     */
    protected function showError($message = '请求频繁稍后尝试', $code = 400, $option = [])
    {

        // 默认赋值 - 错误码
        $code = !$code || intval($code) == 0 ? 400 : $code;
        // 默认赋值 - 错误标题
        $title = empty($option['title']) ? '出错啦' : $option['title'];
        // 默认赋值 - 错误信息
        $message = !$message ? '请求频繁稍后尝试' : $message;
        // 默认赋值 - 错误返回连接
        $redirectList = !isset($option['redirectList']) ? [] : $option['redirectList'];
        // 不是无权限 && 无返回上一页连接添加
        if ($code != 403 && is_array($redirectList)) {
            $redirectList = array_merge($redirectList, [
                '返回上一页' => 'javascript:cancel()'
            ]);
        }

        // 获取当前操作[Module]名称
        $moduleName = empty($option['moduleName']) || !$option['moduleName'] ?
            $this->module->getUniqueId() : $option['moduleName'];

        ### 模板的选择
        // 后台赋值前台错误模板
        if (empty($option['template']) && strstr($moduleName, 'backend')) {
            $template = '@app/views/site/backendError.php';
            $this->layout = '@app/views/layouts/backendMain.php';
        } else if (empty($option['template']) && strstr($moduleName, 'front')) {
            // 前台赋值前台错误模板
            $template = '@app/views/site/frontError.php';
            $this->layout = '@app/views/layouts/frontMain.php';
        } else {
            $template = empty($option['template']) ? '@app/views/site/frontError.php' : $option['template'];
        }

        ### 原因
        // 原因空的就要空数组
        $option['reasons'] = empty($option['reasons']) ? [] : $option['reasons'];
        // 原因不是数组分割为数组
        $option['reasons'] = is_string($option['reasons']) ?
            explode(',', $option['reasons']) : $option['reasons'];
        // 无权限或者未登录默认原因
        if (in_array($code, $this->errorReasons)) {
            $option['reasons'] = array_merge($option['reasons'], $this->errorReasons[$code]);
        } else {
            $option['reasons'] = !empty($option['reasons']) ?
                $option['reasons'] : $this->errorReasons['default'];
            $option['reasons'] = array_merge($option['reasons'], [
                '尝试多次无果，可直接联系站点管理员。'
            ]);
        }

        return $this->render($template, [
            'errorCode' => $code,
            'title' => $title,
            'message' => $message,
            'redirectList' => is_array($redirectList) ? array_filter($redirectList) : $redirectList,
            'reasons' => !empty($option['reasons']) ? $option['reasons'] : []
        ]);
    }
}
